package cn.com.pagoda.ideplugin.common.transcoder;

import cn.hutool.core.util.URLUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Base64;

/**
 * 转码算法枚举
 *
 * @Author xieluxin
 * @Date 2023/7/8 22:39
 * @Version 1.0
 */
public enum TranscoderAlgorithmEnum {
    AES256("AES256", "AES256加密算法", TranscoderTypeEnum.ENCRYPT_AND_DECRYPT_SUB_PANEL
            , (plaintext, key) -> {
        if (key.getBytes(StandardCharsets.UTF_8).length != 32) {
            throw new RuntimeException("AES256算法秘钥长度需要等于32位(字节)");
        }
        Cipher cipher = Cipher.getInstance("AES");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }, (ciphertext, key) -> {
        if (key.getBytes(StandardCharsets.UTF_8).length != 32) {
            throw new RuntimeException("AES256算法秘钥长度需要等于32位(字节)");
        }
        Cipher cipher = Cipher.getInstance("AES");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        byte[] encryptedBytes = Base64.getDecoder().decode(ciphertext);
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }),
    DES("DES", "DES加密算法", TranscoderTypeEnum.ENCRYPT_AND_DECRYPT_SUB_PANEL
            , (plaintext, key) -> {
        if (key.getBytes(StandardCharsets.UTF_8).length != 8) {
            throw new RuntimeException("DES算法秘钥长度需要等于8位(字节)");
        }
        DESKeySpec desKeySpec = new DESKeySpec(key.getBytes(StandardCharsets.UTF_8));
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey secretKey = keyFactory.generateSecret(desKeySpec);

        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);

        byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }, (ciphertext, key) -> {
        if (key.getBytes(StandardCharsets.UTF_8).length != 8) {
            throw new RuntimeException("DES算法秘钥长度需要等于8位(字节)");
        }
        DESKeySpec desKeySpec = new DESKeySpec(key.getBytes(StandardCharsets.UTF_8));
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey secretKey = keyFactory.generateSecret(desKeySpec);

        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(Cipher.DECRYPT_MODE, secretKey);

        byte[] encryptedBytes = Base64.getDecoder().decode(ciphertext);
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }),
    SM4("SM4", "国密SM4加密算法", TranscoderTypeEnum.ENCRYPT_AND_DECRYPT_SUB_PANEL
            , (plaintext, key) -> {
        if (key.getBytes(StandardCharsets.UTF_8).length != 16) {
            throw new RuntimeException("SM4算法秘钥长度需要等于16位(字节)");
        }
        Security.addProvider(new BouncyCastleProvider());
        Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS5Padding");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "SM4");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }, (ciphertext, key) -> {
        if (key.getBytes(StandardCharsets.UTF_8).length != 16) {
            throw new RuntimeException("SM4算法秘钥长度需要等于16位(字节)");
        }
        Security.addProvider(new BouncyCastleProvider());
        Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS5Padding");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "SM4");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(ciphertext));
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }),

    BASE64("Base64", "Base64编码算法", TranscoderTypeEnum.ENCODE_AND_DECODE_SUB_PANEL
            , (plaintext, key) -> Base64.getEncoder().encodeToString(plaintext.getBytes())
            , (ciphertext, key) -> new String(Base64.getDecoder().decode(ciphertext))),
    Unicode("Unicode", "Unicode编码算法", TranscoderTypeEnum.ENCODE_AND_DECODE_SUB_PANEL
            , (plaintext, key) -> StringEscapeUtils.escapeJava(plaintext)
            , (ciphertext, key) -> StringEscapeUtils.unescapeJava(ciphertext)),
    URL_ENCODING("UrlEncoding", "UrlEncoding算法", TranscoderTypeEnum.ENCODE_AND_DECODE_SUB_PANEL
            , (plaintext, key) -> URLUtil.encodeAll(plaintext)
            , (ciphertext, key) -> URLUtil.decode(ciphertext)),
    MD5("MD5", "md5计算哈希", TranscoderTypeEnum.HASH_CALCULATE_SUB_PANEL
            , (plaintext, key) -> DigestUtils.md5Hex(plaintext), null),
    SHA256("SHA256", "SHA256计算哈希", TranscoderTypeEnum.HASH_CALCULATE_SUB_PANEL
            , (plaintext, key) -> DigestUtils.sha256Hex(plaintext), null),
    SM3("SM3", "国密SM3计算哈希", TranscoderTypeEnum.HASH_CALCULATE_SUB_PANEL
            , (plaintext, key) -> {
        byte[] data = plaintext.getBytes();
        SM3Digest digest = new SM3Digest();
        digest.update(data, 0, data.length);
        byte[] output = new byte[digest.getDigestSize()];
        digest.doFinal(output, 0);
        return Hex.toHexString(output);
    }, null),

    ;
    private final String code;
    private final String msg;
    private final TranscoderTypeEnum transcoderTypeEnum;

    /**
     * 正向转换(编码/加密/计算哈希)函数
     */
    private final ForwardTranscodeFunction forwardTranscodeFunction;
    /**
     * 反向转换(解码/解密)函数
     */
    private final ReverseTranscodeFunction reverseTranscodeFunction;

    TranscoderAlgorithmEnum(String code, String message, TranscoderTypeEnum transcoderTypeEnum
            , ForwardTranscodeFunction forwardTranscodeFunction, ReverseTranscodeFunction reverseTranscodeFunction) {
        this.code = code;
        this.msg = message;
        this.transcoderTypeEnum = transcoderTypeEnum;
        this.forwardTranscodeFunction = forwardTranscodeFunction;
        this.reverseTranscodeFunction = reverseTranscodeFunction;
    }

    public String getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public TranscoderTypeEnum getTranscoderTypeEnum() {
        return transcoderTypeEnum;
    }

    public static TranscoderAlgorithmEnum getByCode(String code) {
        if (StringUtils.isBlank(code)) {
            throw new RuntimeException("请选择算法");
        }
        for (TranscoderAlgorithmEnum algorithmEnum : TranscoderAlgorithmEnum.values()) {
            if (algorithmEnum.getCode().equals(code)) {
                return algorithmEnum;
            }
        }
        throw new RuntimeException("未找到对应的算法");
    }

    public ForwardTranscodeFunction getForwardTranscodeFunction() {
        return forwardTranscodeFunction;
    }

    public ReverseTranscodeFunction getReverseTranscodeFunction() {
        return reverseTranscodeFunction;
    }
}
